package com.dns.reset.exec;

import com.dns.reset.core.SSHCommandExecutor;
import com.dns.reset.core.ShellUtils;
import com.dns.reset.core.TelnetUtil;
import com.dns.reset.web.model.DbDnsEntity;
import com.dns.reset.web.model.DbTelnetEntity;
import com.dns.reset.web.service.DbDnsService;
import com.dns.reset.web.service.DbTelnetService;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.io.*;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

@Component
public class ShellExecutor {

    protected static Logger logger = LoggerFactory.getLogger(ShellExecutor.class);
    @Value("${dns.FilePath}")
    private String filepath;
    @Value("${telnet.host}")
    private String host;
    @Value("${telnet.user}")
    private String user;
    @Value("${telnet.passsword}")
    private String password;
    private String deviceName="GD-GZ-QY-C-S12708-CSS";

    @Autowired
    private DbDnsService dbDnsService;
    @Autowired
    private DbTelnetService dbTelnetService;


    public synchronized  void collectDNS()  {
        File file = null;
        InputStreamReader read = null;
        try {
            //每次执行初始化数据。以便后续比较
            dbDnsService.initStart();
            String encoding = "UTF-8";
            file = new File(filepath);
            if (file.isFile() && file.exists()) { // 判断文件是否存在
                read = new InputStreamReader(new FileInputStream(file),
                        encoding);
                // 考虑到编码格式
                BufferedReader bufferedReader = new BufferedReader(read);
                String lineTxt = null;
                int i=dbDnsService.getAtomicInteger();//记录执行次数
                List<String> list=new ArrayList<String>();//结果集
                List<Future<List<String>>> futureList = new ArrayList<Future<List<String>>>();
                logger.info("开始采集IP地址.....");
                ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
                while ((lineTxt = bufferedReader.readLine()) != null) {
                    if (StringUtils.isNotBlank(lineTxt)) {
                        futureList.add(cachedThreadPool.submit(new CallableTask(lineTxt,i)));
                    }
                }
                for(Future<List<String>> future: futureList){
                    //CPU高速轮询：每个future都并发轮循，判断完成状态然后获取结果，
                    // 这一行，是本实现方案的精髓所在。即有10个future在高速轮询，完成一个future的获取结果，就关闭一个轮询
                    while (true) {
                        //获取future成功完成状态
                        if (future.isDone()&& !future.isCancelled()) {
                            list.addAll(future.get());
                            break;//当前future获取结果完毕，跳出while
                        }else {
                            Thread.sleep(10);//每次轮询休息10毫秒（CPU纳秒级），避免CPU高速轮循耗空CPU
                        }
                    }
                }
                logger.info("采集IP地址完成,共 {} 个......",list.size());
                logger.info("开始telnet登陆服务器 {} 执行命令......",host);
                collectResultToTelnet(list);

            } else {
                logger.info("找不到指定的文件");
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        } finally {
            if (read != null) {
                try {
                    read.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    /**
     * 线程内部类
     * @author Guoyl
     *
     */
    class MyThread extends Thread {
        private String telnetCmd;
        private String msg;
        public MyThread(String cmd,String msg ){
            this.telnetCmd=cmd;
            this.msg=msg;
        }
        public void run() {
            sendTelnetCmd(host, user, password, telnetCmd,msg);
        }
    }
    class CallableTask implements Callable<List<String>> {
        private String dns;
        private Integer num;
        private List<String> list;
        public CallableTask(String dns ,Integer num){
            this.dns=dns;
            this.num=num;
        }
        @Override
        public List<String> call() throws Exception {
            list= sendCmd(dns, num);
            return list;
        }
    }


    /**
     * 本地执行命令
     * @param cmd
     * @param num
     */
    public List<String> sendCmd(String cmd,int num) {
       List<String> result= ShellUtils.execCmd("nslookup "+cmd, null);
        List<String> ipList = new ArrayList<String>();
        for (String str : result) {
            if (StringUtils.contains(str, "Address:")) {
                String[] arr = str.split("Address:");
                if(!arr[1].trim().contains("#")) {
                    ipList.add(arr[1].trim());
                }
            }
        }
        addIp(ipList,cmd,num);
        return ipList;
    }

    public void addIp(List<String> ipList,String dns,int num){
        for(String ip :ipList){
            DbDnsEntity dbDnsEntity=new DbDnsEntity();
                dbDnsEntity.setDnsAddress(dns);
                dbDnsEntity.setIp(ip);
                dbDnsEntity.setNo(num);
                dbDnsEntity.setCreateTime(new Date());
                dbDnsEntity.setIsNew(1);
            dbDnsService.save(dbDnsEntity);
            }
    }

    public void collectResultToTelnet(List<String> list){
        try {
            ExecutorService fixedThreadPool  = Executors.newFixedThreadPool(2);
                for (String ip : list) {
                    List<DbTelnetEntity> dbTelnetEntities=dbTelnetService.findAll();
                    DbTelnetEntity entity=new DbTelnetEntity();
                    boolean isAdd=true;
                    for (DbTelnetEntity dbTelnetEntity : dbTelnetEntities) {
                            if(StringUtils.equals(ip,dbTelnetEntity.getIp())){
                                entity=dbTelnetEntity;
                                isAdd=false;
                            }
                    }
                    entity.setDestroyTime(null);
                    entity.setIsExist(1);
                    entity.setIp(ip);
                    dbTelnetService.save(entity);
                    if(isAdd){

                        //发送新增 IP telnet命令
                        MyThread myThread=new MyThread(entity.getTelnetCommand(),"执行新增 IP telnet命令");
                        fixedThreadPool.execute(myThread);
                    }

                }
                //新增对比执行完毕后，执行删除超时ip
            List<DbTelnetEntity> dns= dbTelnetService.findByIsExist(0);
                for(DbTelnetEntity dte:dns){
                    if(dte.getDestroyTime().getTime()<=new Date().getTime()){
                        //发送删除Ip telnet 命令
                        String telnetCmd = "undo rule " + dte.getRuleId();
                        MyThread myThread=new MyThread(telnetCmd,"执行删除超时ip命令");
                        fixedThreadPool.execute(myThread);
                    }
                }
        }catch (Exception e){
            logger.error(e.getMessage());
            e.printStackTrace();
        }
    }


    public String sendTelnetCmd(String host, String user, String pwd,String cmd,String message){
        logger.info(message);
        logger.info("Try to telnet Host:{},User:{},Password：{}，DeviceName:{},Cmd:{}",host,user,pwd,deviceName,cmd);
        TelnetUtil telnet = null;
        String rString = "";
        try {
            telnet = new TelnetUtil(host, 23, user, pwd, deviceName);
            logger.info("执行命令：sys");
            String mString = telnet.sendCommand("sys", 200);
            logger.info("返回" + mString);
            logger.info("执行命令：acl number 3600");
            String aclString = telnet.sendCommand("acl number 3600", 200);
            logger.info("返回" + aclString);
            logger.info("执行了：" + cmd);
            rString = telnet.sendCommand(cmd, 200);
            logger.info("返回" + rString);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        } finally {
            try {
                if (telnet != null) {
                    telnet.disConnect();
                }

            } catch (IOException e) {
                logger.error(e.getMessage(), e);
            }
        }
        return rString;
    }
}
